#pragma once
#include "Connection.hpp"
#include <queue>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <atomic>
#include <functional>
struct Timeout
{
    int _max_idle_time; // 连接池最大空闲时间
    int _conn_timeout;  // 连接池获取连接的超时时间
};

class ConnPool
{
private:
    static ConnPool *_pool;
    // 连接池线程最大数量
    int _thread_max_cnt;
    std::string _ip;                      // mysql的ip地址
    std::string _db;                      // 数据库的名称
    unsigned short _port;                 // mysql端口号3306
    std::string _username;                // mysql用户名
    std::string _password;                // mysql登陆密码
    Timeout _timeout;                     // 各类超时时间属性
    std::queue<Connection *> _conn_queue; // 存储mysql连接队列
    std::mutex _queue_mtx;
    static std::mutex _mtx;          // 检查锁定模式
    std::atomic<int> _conn_cnt;     // 记录连接所创建的connect的数量
    std::condition_variable _cond;
    int _init_size; // 连接池的初始连接量
    int _max_size;  // 连接池的最大连接量
    int _min_size = 1;
private:
    // 创建新连接
    void MakeNewConnection()
    {
        while (true)
        {
            std::unique_lock<std::mutex> lock(_queue_mtx);
             // 如果队列中存在连接则阻塞
             _cond.wait(lock,[this]()
                {
                    return _conn_queue.size() < _min_size;
                });
            //创建新连接
            auto conn = new Connection();
            conn->connect(_ip,_port,_username,_password,_db);
            //刷新活跃时间
            conn->refreshAliveTime();
            _conn_queue.push(conn);
            _conn_cnt.fetch_add(1);
            //唤醒
            _cond.notify_all();
        }
    }
    // 回收旧连接
    void RecycleConnection()
    {
        while(true)
        {
            std::this_thread::sleep_for(std::chrono::seconds(_timeout._max_idle_time));
            //检查有无超时连接
            std::unique_lock<std::mutex> lock(_queue_mtx);
            while(_conn_queue.size())
            {
                auto conn = _conn_queue.front();
                if(conn->getAliveeTime() > _timeout._max_idle_time)
                {
                    _conn_queue.pop();
                    delete conn;
                }
            } 
        }
    }
private:
    ConnPool(const std::string &ip, unsigned short port, const std::string &user,
              const std::string &password, const std::string &db,
              int init_size, int max_size = 1024)
        : _ip(ip),
          _port(port),
          _username(user),
          _password(password),
          _db(db)
    {
        _init_size = init_size;
        _max_size = max_size;
        // 创建一批连接
        for (int i = 0; i < _init_size; ++i)
        {
            Connection *conn = new Connection();
            conn->connect(ip, port, user, password, db);
            LOG(INFO, "初始化连接池完毕,当前已有连接:%d\n", _init_size);
        }
        // 绑定生产者线程--即创建新连接
        std::thread tid_push = std::thread(std::bind(&ConnPool::MakeNewConnection, this));
        tid_push.detach();
        std::thread tid_recy = std::thread(std::bind(&ConnPool::RecycleConnection, this));
        tid_recy.detach();
    }
    // 设置超时属性
    void set_timeout(int max_idle_time,
                     int conn_timeout)
    {
        _timeout._max_idle_time = max_idle_time;
        _timeout._conn_timeout = conn_timeout;
    }

public:
    static ConnPool *GetConnInstance(const std::string &ip,
                                     unsigned short port,
                                     const std::string &user,
                                     const std::string &password,
                                     const std::string &db,
                                     int init_size,
                                     int max_idle_time = 10,
                                     int conn_timeout = 100,
                                     int max_size = 1024)
    {
        if (_pool == nullptr)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            if (_pool == nullptr)
            {
                _pool = new ConnPool(ip, port, user, password, db, init_size, max_size);
                _pool->set_timeout(max_idle_time, conn_timeout);
                LOG(DEBUG, "连接池单例创建成功!\n");
            }
        }
        return _pool;
    }
    Connection::ptr GetAliveConn()
    {
        std::unique_lock<std::mutex> lock(_mtx);
        while(_conn_queue.empty())
        {   
            //等待建造时间
            if(std::cv_status::timeout == _cond.wait_for(lock, std::chrono::milliseconds(_timeout._conn_timeout)))
            {
                if(_conn_queue.empty())
                {
                    LOG(ERROR,"连接池中没有可用的连接!\n");
                    return nullptr;
                }
            }
        }
        Connection::ptr conn(_conn_queue.front(),[&](Connection* conn){
                    std::unique_lock<std::mutex> lock(_mtx);
                    conn->refreshAliveTime();
                    _conn_queue.push(conn);
            });
        _conn_queue.pop();
        _cond.notify_all();
        return conn;
    }
};
std::mutex ConnPool::_mtx;
ConnPool *ConnPool::_pool = nullptr;